#pragma once

#include <iostream>
#include <string>
#include <pthread.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
#include "Socket.hpp"
#include "Log.hpp"

static const int defaultport = 8080;
const std::string wwwroot = "./wwwroot";
const std::string sep = "\r\n";
const std::string homepage = "index.html";

class HttpServer;

class ThreadDate
{
public:
    ThreadDate(int sockfd, HttpServer *svr):_sockfd(sockfd),_svr(svr){}
public:
    int _sockfd;
    HttpServer *_svr;
};

class HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        //读到的是一个字符串，中间用\r\n区分开
        while(true)
        {
            //读到\r\n，做切割
            std::size_t pos = req.find(sep);
            //没读到，意味着读取结束，跳出循环
            if(pos == std::string::npos) break;
            //读到第一个字符串
            std::string temp = req.substr(0, pos);
            //读到空行，也结束
            if(temp.empty()) break;
            //将读到的字符串push到vector
            req_header.push_back(temp);
            //读到一个，擦除一个，周而复始
            req.erase(0,pos+sep.size());
        }
        //空行之后，就是正文，给到text
        text = req;
    }

    void Parse()
    {
        //将反序列化后的内容进行解析
        //请求行之间是用空格切割开，可以采用stringstream简单处理
        std::stringstream ss(req_header[0]);
        ss >> method >> url >>http_version;
        file_path = wwwroot;
        //因此可将请求行切割开成method/url/url 这三个是固定的，必须按顺序

        //如果url是根目录或者根目下的网页，则访问该网页
        if(url == "/" || url == "./index.html")
        {
            file_path += "/" ;
            file_path += homepage;
        }
        //如果url是指定网页，那就去指定网页
        else file_path += url;

        //用来确定访问的内容后缀格式，rfind从后往前找
        auto pos = file_path.rfind(".");
        //若没找到，默认访问的是网页html
        if(pos == std::string::npos) suffix = ".html";
        //找到了，是啥就是啥
        else suffix = file_path.substr(pos);
    }

    void DebugPrintf()
    {
        //对请求请进打印
        for(auto& line: req_header)
        {
            std::cout << "-----------------------------------------" << std::endl;
            std::cout << line << std::endl;
        }
        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
    }
public:
    std::vector<std::string> req_header;//用来存储请求行和请求头的内容
    std::string text;//用来存储正文内容

    //解析之后的结果
    std::string method;//请求方法/GER or POST
    std::string url;//访问路径
    std::string http_version; //http版本/1.0/1.1
    std::string file_path;//文件路径
    std::string suffix;//数据类型
};


class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport):_port(port)
    {
        content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});
        content_type.insert({".jpeg", "image/jpeg"});
    }

    bool Start()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        while(true)
        {
            //获取客户端的信息
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(clientip, clientport);
            if(sockfd < 0) continue;
            lg(Info,"get a new link connect, sockfd: %d", sockfd);

            //把接受后的sockfd传给线程
            ThreadDate* td = new ThreadDate(sockfd,this);
            pthread_t tid;
            pthread_create(&tid, nullptr, ThreadRun, td);
        }   
    }

    //参数给的是路劲
    static::std::string ReadHtmlContent(const std::string& htmlpath)
    {
        //C++文件读取，从文件里读取，二进制读取
        std::ifstream in(htmlpath, std::ios::binary);
        if(!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        // in.seekg(0,std::ios_base::end);
        // auto len = in.tellg();
        // in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());

        //含字符串行读取
        // std::string content;
        // std::string line;
        // while(std::getline(in,line))
        // {
        //     content += line;
        // }

        in.close();
        return content;
    }

    std::string SuffixToDesc(const std::string &suffix)
    {
        //确定范文的类型数据
        auto iter = content_type.find(suffix);
        if(iter == content_type.end()) return content_type[".html"];
        else return content_type[suffix];

        // auto iter = content_type.find(suffix);
        // if(iter == content_type.end()) return content_type[".html"];
        // else return content_type[suffix];
    }

    void HandlerHttp(int sockfd)
    {
        //创建一个大一点的缓冲区，每次从客户端收到的信息就放入buufer
        char buffer[10240];
        //和read差不多，多了一个参数0，表示阻塞等待
        ssize_t n = recv(sockfd,buffer, sizeof(buffer), 0);
        if(n > 0)
        {
            //n为读到的字节数，
            buffer[n] = 0;
            std::cout << buffer;//假设我们读到了一个完整的http
            
            //创建一个请求对象，让请求对象进行反序列化和解析
            HttpRequest req;
            //反序列化
            req.Deserialize(buffer);
            //解析
            req.Parse();
            //req.DebugPrintf();

            // std::string url = "a/b/c";
            // std::string path = wwwroot + url;

            //返回响应过程
            //定义内容字符串string
            std::string text;
            bool ok = true;
            //从这个函数里读取，返回内容
            text = ReadHtmlContent(req.file_path);

            //如果内容为空，意味着没读到，访问错了，给她404页面
            if(text.empty())
            {
                ok = false;
                //访问404
                std::string err_html = wwwroot +"/" + "err.html";
                text = ReadHtmlContent(err_html);
            }

            //相应行
            std::string response_line;
            //如果上面没读取到，ok==false，那就让相应行等于下面
            if(ok == false) response_line ="HTTP/1.0 404 Not Found\r\n";
            //读取到了，相应行就是正确的
            else  response_line ="HTTP/1.0 200 OK\r\n";

            //response_line = "HTTP/1.0 302 Found\r\n";
            std::string block_line = "\r\n";
            //std::string response_header = "Content-Length: " + std::to_string(text.size()) + "\r\n" + "Location: https://www.bilibili.com";
            std::string response_header = "Content-Length: " + std::to_string(text.size()) + "\r\n" +"Content-Type: " + SuffixToDesc(req.suffix) + "\r\n";
            response_header += "Set-Cookie: name=liny-master";
            response_header += "\r\n";
            response_header += "Set-Cookie: Company=Kugou_music";
            response_header += "\r\n";
            response_header += "Set-Cookie: University=GDUT";
            response_header += "\r\n";

            //响应=相应行+响应头+空行+内容
            std::string response = response_line + response_header + block_line + text;

            //读到的内容发送给客户端
            send(sockfd,response.c_str(), response.size(),0);
        }
        close(sockfd);
    }
    
    //线程执行该任务函数
    static void* ThreadRun(void* args)
    {
        //将线程与主线程进行分离
        pthread_detach(pthread_self());
        ThreadDate* td = static_cast<ThreadDate*>(args);
        //去具体调用执行的函数，低内聚
        td->_svr->HandlerHttp(td->_sockfd);
        delete td;
        return nullptr;
    }

    ~HttpServer(){}
private:
    uint16_t _port;
    Sock _listensock;
    std::unordered_map<std::string,std::string> content_type;
};